package behaviour;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
import java.util.Vector;

import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;

public class PacmanFinder implements Strategy {
	
	private Coordinate currentCoordinate;
	private Coordinate pacmanCoordinate;
	private ArrayList<Direction> path = new ArrayList<Direction>();
	private Maze maze;
	public Maze collaborativeDiffusionMaze;
	private boolean collaborativeDiffusionMazeFilledWithPacmanAttractorField = false;
	private boolean collaborativeDiffusionMazeFilledWithGhostDeflectorField = false;
	public PacmanFinder(Maze maze){
		this.maze=maze;
		collaborativeDiffusionMaze = new Maze(10);
		pacmanCoordinate = new Coordinate(maze.getMinX()+maze.getConfirmedXsize()/2,maze.getMinY()+maze.getConfirmedYsize()/2);
	}
	@Override
	public Direction nextDirection(Location loc, Maze[] mazes, Coordinate[] ghosts) {
		if(!collaborativeDiffusionMazeFilledWithPacmanAttractorField) setPacmanLocation(new Coordinate(maze.getMinX()+maze.getConfirmedXsize()/2,maze.getMinY()+maze.getConfirmedYsize()/2));
		currentCoordinate=maze.getCoordinateOfLocation(loc);
		if(!collaborativeDiffusionMazeFilledWithPacmanAttractorField) fillCollaborativeDiffusionMazeWithPacmanAttractorField(mazes);
		//fillCollaborativeDiffusionMazeWithGhostDeflectorField(mazes, ghosts);
		TreeSet<ValuedDirection> options = new TreeSet<ValuedDirection>();
		int valCURRENT = collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y, Direction.NORTH, true) - collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y, Direction.EAST, true);
		int valNORTH = collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y+1, Direction.NORTH, true) - collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y+1, Direction.EAST, true);
		int valEAST = collaborativeDiffusionMaze.read(currentCoordinate.x+1, currentCoordinate.y, Direction.NORTH, true) - collaborativeDiffusionMaze.read(currentCoordinate.x+1, currentCoordinate.y, Direction.EAST, true);
		int valSOUTH = collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y-1, Direction.NORTH, true) - collaborativeDiffusionMaze.read(currentCoordinate.x, currentCoordinate.y-1, Direction.EAST, true);
		int valWEST = collaborativeDiffusionMaze.read(currentCoordinate.x-1, currentCoordinate.y, Direction.NORTH, true) - collaborativeDiffusionMaze.read(currentCoordinate.x-1, currentCoordinate.y, Direction.EAST, true);
		options.add(new ValuedDirection(null,valCURRENT)); //CURRENT
		
		//check NORTH
		int wallval = maze.read(currentCoordinate.x, currentCoordinate.y, Direction.NORTH, true);
		for(Maze m : mazes) {
			int newwallval = m.read(currentCoordinate.x, currentCoordinate.y, Direction.NORTH, false);
			if(newwallval==1) { wallval = 1; break; }
			if(newwallval==2) { wallval = 2; }
		}
		if(wallval==2) options.add(new ValuedDirection(Direction.NORTH,valNORTH)); //NORTH
		
		//check EAST
		wallval = maze.read(currentCoordinate.x, currentCoordinate.y, Direction.EAST, true);
		for(Maze m : mazes) {
			int newwallval = m.read(currentCoordinate.x, currentCoordinate.y, Direction.EAST, false);
			if(newwallval==1) { wallval = 1; break; }
			if(newwallval==2) { wallval = 2; }
		}
		if(wallval==2) options.add(new ValuedDirection(Direction.EAST,valEAST)); //EAST
		
		//check SOUTH
		wallval = maze.read(currentCoordinate.x, currentCoordinate.y, Direction.SOUTH, true);
		for(Maze m : mazes) {
			int newwallval = m.read(currentCoordinate.x, currentCoordinate.y, Direction.SOUTH, false);
			if(newwallval==1) { wallval = 1; break; }
			if(newwallval==2) { wallval = 2; }
		}
		if(wallval==2) options.add(new ValuedDirection(Direction.SOUTH,valSOUTH)); //SOUTH
		
		//check WEST
		wallval = maze.read(currentCoordinate.x, currentCoordinate.y, Direction.WEST, true);
		for(Maze m : mazes) {
			int newwallval = m.read(currentCoordinate.x, currentCoordinate.y, Direction.WEST, false);
			if(newwallval==1) { wallval = 1; break; }
			if(newwallval==2) { wallval = 2; }
		}
		if(wallval==2) options.add(new ValuedDirection(Direction.WEST,valWEST)); //WEST
		
		return options.last().d;
	}
	
	//TODO: add usage of other mazes
	private void fillCollaborativeDiffusionMazeWithPacmanAttractorField(Maze[] mazes) {
		Vector<Coordinate> passed = new Vector<Coordinate>();
		Vector<Coordinate> active = new Vector<Coordinate>();
		active.add(pacmanCoordinate);
		char currentHeuristicValue = 0; 
		while(active.size()>0) { //as long as there might be longer paths
			Vector<Coordinate> temp = new Vector<Coordinate>();
			for(Coordinate c : active) {
				passed.add(c);
				collaborativeDiffusionMaze.write(c.x, c.y, Direction.NORTH, currentHeuristicValue, true);
				//NORTH
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.NORTH,true); //get the wall we're looking at
					for(Maze m : mazes) {
						int newwallval = m.read(c.x, c.y, Direction.NORTH, false);
						if(newwallval==1) { val = 1; break; }
						if(newwallval==2) { val = 2; }
					}
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x,c.y+1);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//EAST
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.EAST,true); //get the wall we're looking at
					for(Maze m : mazes) {
						int newwallval = m.read(c.x, c.y, Direction.EAST, false);
						if(newwallval==1) { val = 1; break; }
						if(newwallval==2) { val = 2; }
					}
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x+1,c.y);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//SOUTH
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.SOUTH,true); //get the wall we're looking at
					for(Maze m : mazes) {
						int newwallval = m.read(c.x, c.y, Direction.SOUTH, false);
						if(newwallval==1) { val = 1; break; }
						if(newwallval==2) { val = 2; }
					}
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x,c.y-1);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//WEST
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.WEST,true); //get the wall we're looking at
					for(Maze m : mazes) {
						int newwallval = m.read(c.x, c.y, Direction.WEST, false);
						if(newwallval==1) { val = 1; break; }
						if(newwallval==2) { val = 2; }
					}
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x-1,c.y);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				
			}
			currentHeuristicValue--;
			currentHeuristicValue--;
			active = temp; //prepare for the next iteration of the while loop
		}
		collaborativeDiffusionMazeFilledWithPacmanAttractorField = true;
	}
	
	//add other mazes...
	private void fillCollaborativeDiffusionMazeWithGhostDeflectorField(Maze[] mazes, Coordinate[] ghosts) {
		Vector<Coordinate> passed = new Vector<Coordinate>();
		Vector<Coordinate> active = new Vector<Coordinate>();
		for(int i=0; i<mazes.length; i++) {
			Coordinate ghostCoordinate = mazes[i].reverseTransform(ghosts[i]);
			active.add(ghostCoordinate);
			collaborativeDiffusionMaze.write(ghostCoordinate.x, ghostCoordinate.y, Direction.EAST, collaborativeDiffusionMaze.read(ghostCoordinate.x, ghostCoordinate.y, Direction.NORTH, true), true);
		}
		char currentHeuristicValue = 0;
		boolean init = true;
		while(active.size()>0 & currentHeuristicValue!=0) { //as long as there might be longer paths
			Vector<Coordinate> temp = new Vector<Coordinate>();
			for(Coordinate c : active) {
				passed.add(c);
				if(!init) collaborativeDiffusionMaze.write(c.x, c.y, Direction.EAST, currentHeuristicValue, true);
				else init = false;
				//NORTH
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.NORTH,true); //get the wall we're looking at
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x,c.y+1);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//EAST
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.EAST,true); //get the wall we're looking at
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x+1,c.y);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//SOUTH
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.SOUTH,true); //get the wall we're looking at
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x,c.y-1);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				//WEST
				if(true) { //check whether we're not going out of bounds
					int val = maze.read(c.x,c.y,Direction.WEST,true); //get the wall we're looking at
					//create a new Coordinate with the coordinate we're adding
					Coordinate t = new Coordinate(c.x-1,c.y);
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					}
				}
				
			}
			currentHeuristicValue--;
			active = temp; //prepare for the next iteration of the while loop
		}
		collaborativeDiffusionMazeFilledWithGhostDeflectorField = true;
	}
	
	public void setPacmanLocation(Coordinate pacmanCoordinate) {
		setCoordinatePacman(pacmanCoordinate);
	}
	
	@Override
	public boolean exploreEverything() {
		// TODO Auto-generated method stub
		return false;
	}
	
	private class ValuedDirection implements Comparable<ValuedDirection> {
		public Direction d;
		public int val;
		public ValuedDirection(Direction d, int valCURRENT) {
			this.d = d;
			this.val = valCURRENT;
		}
		@Override
		public int compareTo(ValuedDirection o) {
			return (val<o.val)?-1:((val>o.val)?1:0);
		}
	}

	@Override
	public void setCoordinatePacman(Coordinate coordinate) {
		if(pacmanCoordinate==null) return;
		this.pacmanCoordinate = pacmanCoordinate;
		this.collaborativeDiffusionMazeFilledWithPacmanAttractorField = false;		
	}
}
